//
// Created by TongHua on 2020/12/21.
//

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "../ThreadPool/ThreadPool.h"
#include "../Http/HttpConn.h"
#include "../Logger/Logger.h"
#include "../SQL/sql_connection_pool.hpp"
#include "../Utils/ListTime.h"

#define MAX_FD 65535
#define MAX_EVENT_NUMBER 10000
#define TIMESLOT 1

using namespace std;

//这三个函数在http_conn.cpp中定义，改变链接属性
extern int Addfd(int epollfd, int fd, bool one_shot);
extern int Removefd(int epollfd, int fd);
extern int SetNoblocking(int fd);

static int pipefd[2];
static int epollfd = 0;
static sort_timer_lst timer_list;


//信号处理函数
void sig_handler(int sig)
{
    //为保证函数的可重入性，保留原来的errno
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1], (char *)&msg, 1, 0);
    errno = save_errno;
}

//设置信号函数
void addsig(int sig, void(handler)(int), bool restart = true)
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof(sa));
    sa.sa_handler = handler;
    if (restart)
        sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig, &sa, NULL) != -1);
}

/**
 * 定时器触发函数，用于不断触发STGALRM信号
 */
void timer_handle()
{
    timer_list.tick();
    alarm( TIMESLOT );
}

/**
 *
 * @param _user_data
 */
void cd_func(client_data* _user_data)
{
    epoll_ctl( epollfd, EPOLL_CTL_DEL, _user_data->sockfd, 0 );
    assert( _user_data );
    close( _user_data->sockfd );
    HttpConn::m_user_count--;
    LOG_INFO("close fd %d",_user_data->sockfd);
    Logger::Instance()->Flush();
}

void ShowError( int _connfd, const char* _info )
{
    printf( "%s", _info );
    send( _connfd, _info, strlen( _info ), 0 );
    close( _connfd );
}


int main(int argc, char const *argv[])
{
    if(argc <= 1)
    {
        printf("usage:%s ip_address port_number\n",basename(argv[0]));
        return 1;
    }

    // 异步日志
    Logger::Instance()->Init("ServerLog",2000,800000,8);

    int port = atoi(argv[1]);

    addsig( SIGPIPE, SIG_IGN );

    // 创建数据池
    ConnectionPool *conn_pool = ConnectionPool::GetInstance();
    conn_pool->Init("localhost", "root", "poii0987", "Test", 3306, 8);

    // 创建线程池
    ThreadPool< HttpConn >* pool = NULL;
    try
    {
        pool = new ThreadPool< HttpConn >;
        pool->Init(conn_pool);
    }
    catch ( ... )
    {
        printf( " Create Thread Pool Error \n");
        return  1;
    }
    HttpConn* users = new HttpConn[ MAX_FD ];
    assert( users );
    // 初始化数据库信息
    users->InitMySQLResult(conn_pool);

    int user_count = 0;

    int listenfd = socket(PF_INET,SOCK_STREAM,0);
    assert(listenfd >= 0);

    int ret = 0;
    sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(listenfd,(sockaddr*)&address,sizeof(address));
    assert(ret != -1);

    ret = listen(listenfd,5);
    assert(ret != -1);

    // 初始化事件表
    epoll_event events[ MAX_EVENT_NUMBER ];
    int epollfd = epoll_create( 5 );
    assert( epollfd != -1 );
    Addfd( epollfd, listenfd, false );
    HttpConn::m_epollfd = epollfd;

    // 创建管道通信
    ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pipefd );
    assert( ret != -1 );
    SetNoblocking( pipefd[1] );
    Addfd( epollfd, pipefd[0], false );

    addsig( SIGALRM, sig_handler, false );
    addsig( SIGTERM, sig_handler, false );
    bool stop_server = false;

    // 定时器队列,大小为整个描述符的大小，方便快速查找
    client_data* users_timer = new client_data[MAX_FD];
    bool time_out = false;
    alarm( TIMESLOT );

    while ( !stop_server )
    {
        int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER , -1);
        if ( ( number < 0 ) && ( errno != EINTR ) )
        {
            printf( "epoll failure \n" );
            break;
        }
        for ( int i = 0; i < number; ++i )
        {
            int sockfd = events[ i ].data.fd;
            if ( sockfd == listenfd )
            {
                sockaddr_in client_address;
                socklen_t client_lenght = sizeof( client_address );
                while (1)
                {
                    int connfd = accept( listenfd, (sockaddr*)&client_address, &client_lenght );
                    if ( connfd < 0 )
                    {
                        printf( "error is %d\n", errno );
                        break;
                    }
                    if ( HttpConn::m_user_count >= MAX_FD )
                    {
                        ShowError( connfd, "Internal server busy" );
                        break;
                    }
                    // 初始化http连接信息
                    users[connfd].Init( connfd, client_address );
                    // 将连接加入到定时器中
                    users_timer[connfd].address = client_address;
                    users_timer[connfd].sockfd = connfd;
                    util_timer* timer = new util_timer;
                    timer->user_data = &users_timer[connfd];
                    timer->cb_func = cd_func;
                    time_t cur = time(NULL);
                    timer->expire = cur + 3 * TIMESLOT;
                    users_timer[connfd].timer = timer;
                    timer_list.add_timer(timer);
                }
                continue;
            }else if ( events[ i ].events & ( EPOLLHUP | EPOLLRDHUP | EPOLLERR ) )
            {
                // 有客户端关闭连接，所以需要从定时器链表中删除
                util_timer* timer = users_timer[sockfd].timer;
                timer->cb_func(&users_timer[sockfd]);
                if(timer)
                {
                    timer_list.del_timer(timer);
                }
            }else if ( (sockfd == pipefd[0]) && (events[i].events & EPOLLIN) )
            {
                // 信号处理
                int sig;
                char signals[1024];
                ret = recv(pipefd[0],signals,sizeof(signals),0);
                if ( ret == -1 )
                {
                    continue;
                }else if ( ret == 0 )
                {
                    continue;
                }else
                {
                    for ( int i = 0; i < ret; ++i )
                    {
                        switch ( signals[i] ) {
                            case SIGALRM:
                                time_out = true;
                                break;
                            case SIGTERM:
                                stop_server = true;
                                break;
                        }
                    }
                }
            }else if ( events[i].events & EPOLLIN )
            {
                // 接收到客户端的消息
                util_timer* timer = users_timer[sockfd].timer;
                if ( users[sockfd].ReadOnce() )
                {
                    LOG_INFO("deal with the client %s",inet_ntoa(users[sockfd].GetAddress()->sin_addr));
                    Logger::Instance()->Flush();
                    // 将请求放入队列中等待处理
                    pool->Append( users + sockfd );
                    if ( timer )
                    {
                        time_t cur = time(NULL);
                        timer->expire = cur + 3 * TIMESLOT;
                        LOG_INFO("%s","adjust timer once");
                        Logger::Instance()->Flush();
                        timer_list.adjust_timer(timer);
                    }
                }else
                {
                    timer->cb_func(&users_timer[sockfd]);
                    if (timer)
                    {
                        timer_list.del_timer(timer);
                    }
                }
            }else if ( events[i].events & EPOLLOUT ) {
                util_timer* timer = users_timer[sockfd].timer;
                if ( users[sockfd].Write() )
                {
                    time_t cur = time(NULL);
                    timer->expire = cur + 3 * TIMESLOT;
                    LOG_INFO("%s","adjust timer once");
                    Logger::Instance()->Flush();
                    timer_list.adjust_timer(timer);
                }else
                {
                    timer->cb_func(&users_timer[sockfd]);
                    if (timer)
                    {
                        timer_list.del_timer(timer);
                    }
                }
            }
        }
        if (time_out)
        {
            timer_handle();
            time_out = false;
        }
    }
    close(epollfd);
    close(listenfd);    // 遵循谁打开谁关闭的原则
    delete[] users;
    delete pool;
    return 0;
}
